#include "KeyLock.h"

#include <memory>

#include <atlstr.h>
#include <atltypes.h>

#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "winmm.lib")

#define PACKAGE_NAME TEXT("HNSFNETKL")

#define CONFIG_NAME TEXT("AppSettings.ini")

// 检查开机启动ID
#define IDACTION_CHECK (WM_USER + 0)
// 添加开机启动ID
#define IDACTION_APPEND (WM_USER + 1)
// 取消开机启动ID
#define IDACTION_CANCEL (WM_USER + 2)

// 显示超时事件ID
#define IDEVENT_DISPLAY (WM_USER + 1)
// 按键扫描事件ID
#define IDEVENT_SCANNING (WM_USER + 2)
// 大写超时事件ID
#define IDCAPS_TIMEOUT (WM_USER + 3)
// 数字超时事件ID
#define IDNUM_TIMEOUT (WM_USER + 4)

// 大写锁定托盘图标ID
#define IDNOTIFY_CAPSLOCK (WM_USER + 1)
// 数字锁定托盘图标ID
#define IDNOTIFY_NUMLOCK (WM_USER + 2)

// 大写锁定托盘事件ID
#define UMNOTIFY_CAPSLOCK (WM_USER + 1)
// 数字锁定托盘事件ID
#define UMNOTIFY_NUMLOCK (WM_USER + 2)

// 秒数转毫秒数
#define MILISECONDS(SECONDS) (SECONDS * 1000)

// 键盘处理钩子
static HHOOK ghKeyboardHook{ NULL };

// 大写锁定键配置
static RUNTIMEPARAMS gCapsLockSetup;

// 数字锁定键配置
static RUNTIMEPARAMS gNumLockSetup;

// 大写锁定计时器
static UINT_PTR gCapsTimerId;

// 指示器窗口句柄
static HWND ghWndIndicator;

// 指示器托盘菜单
static HMENU ghMenuIndicator;

// 大写键锁菜单文字
static TCHAR gszFileCaps[MAX_PATH];

// 数字键锁菜单文字
static TCHAR gszFileNum[MAX_PATH];

VOID DrawImageIcon(RUNTIMEPARAMS* pRuntimeParams, const SHORT wCurrentLockSet)
{
	Gdiplus::Image* pDeskImage = NULL;
	NOTIFYICONDATA* pTrayIcon = NULL;
	const SHORT wLastLockSet = pRuntimeParams->LockSet;
	if (wCurrentLockSet)
	{
		if (wCurrentLockSet == LSFW_LOCK && wLastLockSet != LSFW_LOCK)
		{
			pRuntimeParams->LockSet = LSFW_LOCK;
			pDeskImage = pRuntimeParams->LockDeskImage;
			pTrayIcon = &pRuntimeParams->LockTrayIcon;
		}
		else if (wCurrentLockSet == LSFW_UNLOCK && wLastLockSet != LSFW_UNLOCK)
		{
			pRuntimeParams->LockSet = LSFW_UNLOCK;
			pDeskImage = pRuntimeParams->UnlockDeskImage;
			pTrayIcon = &pRuntimeParams->UnlockTrayIcon;
		}
		else
		{
			return;
		}
	}
	else
	{
		switch (wLastLockSet)
		{
		case LSFW_LOCK:
			pDeskImage = pRuntimeParams->LockDeskImage;
			pTrayIcon = &pRuntimeParams->LockTrayIcon;
			break;
		case LSFW_UNLOCK:
			pDeskImage = pRuntimeParams->UnlockDeskImage;
			pTrayIcon = &pRuntimeParams->UnlockTrayIcon;
			break;
		default:
			return;
		}
	}

	if (pDeskImage)
	{
		LONG nWidth = pDeskImage->GetWidth();
		LONG nHeight = pDeskImage->GetHeight();

		HDC hdcScreen = GetDC(NULL);
		HDC hdcMemory = CreateCompatibleDC(hdcScreen);
		HBITMAP hNewBitmap = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);
		HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMemory, hNewBitmap);

		Gdiplus::Graphics gdiGraphics(hdcMemory);

		gdiGraphics.DrawImage(pDeskImage, 0, 0, nWidth, nHeight);

		BLENDFUNCTION BlendFunc{ 0 };
		BlendFunc.BlendOp = AC_SRC_OVER;
		BlendFunc.SourceConstantAlpha = 0xff;
		BlendFunc.AlphaFormat = AC_SRC_ALPHA;
		CSize Size(nWidth, nHeight);
		CPoint SrcPoint(0, 0);
		UpdateLayeredWindow(ghWndIndicator, hdcScreen, &pRuntimeParams->Position, &Size, hdcMemory, &SrcPoint, 0, &BlendFunc, ULW_ALPHA);
		
		ShowWindowAsync(ghWndIndicator, SW_SHOW);

		SelectObject(hdcMemory, hOldBitmap);
		DeleteObject(hNewBitmap);
		DeleteDC(hdcMemory);
		ReleaseDC(NULL, hdcScreen);

		if (pRuntimeParams->Display > 0)
		{
			SetTimer(ghWndIndicator, IDEVENT_DISPLAY, MILISECONDS(pRuntimeParams->Display), NULL);
		}
	}

	if (pTrayIcon && pTrayIcon->hIcon)
	{
		Shell_NotifyIcon(NIM_MODIFY, pTrayIcon);
	}

	if (pRuntimeParams->Section && wCurrentLockSet)
	{ // 单击托盘不播放音效
		CString strCommand;
		strCommand.Format(TEXT("play %s from 0"), pRuntimeParams->Section);
		mciSendString(strCommand, NULL, 0, NULL);
	}
}

VOID LoadParameters(CONST TCHAR* lpCurrentDirectory, RUNTIMEPARAMS* pRuntimeParams)
{
	ATLASSERT(ghWndIndicator != NULL);
	CString strConfigPath = CString(lpCurrentDirectory) + CString(TEXT("\\")) + CString(CONFIG_NAME);

	BOOL fCapsLockLoading = (pRuntimeParams == &gCapsLockSetup); // 大写锁定参数加载中
	UINT nTrayID = fCapsLockLoading ? IDNOTIFY_CAPSLOCK : IDNOTIFY_NUMLOCK;
	UINT nTrayUM = fCapsLockLoading ? UMNOTIFY_CAPSLOCK : UMNOTIFY_NUMLOCK;
	SHORT wKeyState = fCapsLockLoading ? GetKeyState(VK_CAPITAL) : GetKeyState(VK_NUMLOCK);
	pRuntimeParams->Section = fCapsLockLoading ? const_cast<TCHAR*>(TEXT("CapsLock")) : const_cast<TCHAR*>(TEXT("NumLock"));

	TCHAR szTemporaryData[MAX_PATH]{ 0 };
	// # Lock 与 Unlock 图标位置(默认左 30px 上 30px)
	GetPrivateProfileString(pRuntimeParams->Section, TEXT("Position"), TEXT("0,0"), szTemporaryData, _countof(szTemporaryData), strConfigPath);
	_stscanf_s(szTemporaryData, TEXT("%d,%d"), &pRuntimeParams->Position.x, &pRuntimeParams->Position.y);

	// 对齐方式(HL: 水平居左 HC: 水平居中 HR: 水平居右 VT: 垂直居上 VC: 垂直居中 VB: 垂直居下)
	ZeroMemory(szTemporaryData, sizeof(szTemporaryData));
	GetPrivateProfileString(pRuntimeParams->Section, TEXT("Alignment"), TEXT("HC|VC"), szTemporaryData, _countof(szTemporaryData), strConfigPath);
	
	TCHAR* pszContexts[2];
	pszContexts[0] = _tcstok_s(szTemporaryData, TEXT("|"), (TCHAR**)&pszContexts[1]);
	CString strContexts[2]{ pszContexts[0], pszContexts[1] };

	// # Lock 与 Unlock 置顶图标(左 Lock 右 Unlock)
	ZeroMemory(szTemporaryData, sizeof(szTemporaryData));
	GetPrivateProfileString(pRuntimeParams->Section, TEXT("DeskImage"), CString(pRuntimeParams->Section) + CString(".png"), szTemporaryData, _countof(szTemporaryData), strConfigPath);
	
	CString strDeskImagePath = (_tcsstr(szTemporaryData, TEXT(":\\")) || _tcsstr(szTemporaryData, TEXT(":/"))) ? szTemporaryData : (CString(lpCurrentDirectory) + CString("\\") + szTemporaryData);
	if (PathFileExists(strDeskImagePath))
	{
		std::shared_ptr<Gdiplus::Image> pfsDeskImage(Gdiplus::Image::FromFile(strDeskImagePath));
		int nDeskImageWidth = pfsDeskImage->GetWidth() / 2;
		int nDeskImageHeight = pfsDeskImage->GetHeight();

		int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
		int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
		for (size_t i = 0; i < _countof(strContexts); i++)
		{
			strContexts[i].Trim().MakeUpper();
			if (TEXT("HC") == strContexts[i])
			{
				pRuntimeParams->Position.x += (nScreenWidth - nDeskImageWidth) / 2;
			}
			else if (TEXT("HR") == strContexts[i])
			{
				pRuntimeParams->Position.x += (nScreenWidth - nDeskImageWidth);
			}
			else if (TEXT("VC") == strContexts[i])
			{
				pRuntimeParams->Position.y += (nScreenHeight - nDeskImageHeight) / 2;
			}
			else if (TEXT("VB") == strContexts[i])
			{
				pRuntimeParams->Position.y += (nScreenHeight - nDeskImageHeight);
			}
		}

		pRuntimeParams->LockDeskImage = new Gdiplus::Bitmap(nDeskImageWidth, nDeskImageHeight, PixelFormat32bppARGB);
		pRuntimeParams->UnlockDeskImage = new Gdiplus::Bitmap(nDeskImageWidth, nDeskImageHeight, PixelFormat32bppARGB);
		std::shared_ptr<Gdiplus::Graphics> pgDeskImageLock(Gdiplus::Graphics::FromImage(pRuntimeParams->LockDeskImage));
		std::shared_ptr<Gdiplus::Graphics> pgDeskImageUnlock(Gdiplus::Graphics::FromImage(pRuntimeParams->UnlockDeskImage));

		pgDeskImageLock->DrawImage(pfsDeskImage.get(), Gdiplus::Rect(0, 0, nDeskImageWidth, nDeskImageHeight), 0, 0, nDeskImageWidth, nDeskImageHeight, Gdiplus::Unit::UnitPixel);
		pgDeskImageUnlock->DrawImage(pfsDeskImage.get(), Gdiplus::Rect(0, 0, nDeskImageWidth, nDeskImageHeight), nDeskImageWidth, 0, nDeskImageWidth, nDeskImageHeight, Gdiplus::Unit::UnitPixel);
	}
	// # Lock 与 Unlock 窗口隐藏时间(单位：秒)
	pRuntimeParams->Display = GetPrivateProfileInt(pRuntimeParams->Section, TEXT("Display"), 1, strConfigPath);
	
	// # 大写锁定与数字锁定关闭或开启时间(单位：秒)
	pRuntimeParams->Timeout = GetPrivateProfileInt(pRuntimeParams->Section, TEXT("Timeout"), 0, strConfigPath);

	// # Lock 与 Unlock 托盘图标(左边 Lock 右边 Unlock)
	ZeroMemory(szTemporaryData, sizeof(szTemporaryData));
	GetPrivateProfileString(pRuntimeParams->Section, TEXT("TrayIcon"), CString(pRuntimeParams->Section) + CString(".png"), szTemporaryData, _countof(szTemporaryData), strConfigPath);
	
	CString strTrayIconPath = (_tcsstr(szTemporaryData, TEXT(":\\")) || _tcsstr(szTemporaryData, TEXT(":/"))) ? szTemporaryData : (CString(lpCurrentDirectory) + CString("\\") + szTemporaryData);
	if (PathFileExists(strTrayIconPath))
	{
		std::shared_ptr<Gdiplus::Bitmap> pfsTrayIcon(Gdiplus::Bitmap::FromFile(strTrayIconPath));
		int nTrayIconWidth = pfsTrayIcon->GetWidth() / 2;
		int nTrayIconHeight = pfsTrayIcon->GetHeight();
		Gdiplus::Bitmap LockTrayIcon(nTrayIconWidth, nTrayIconHeight, PixelFormat32bppARGB);
		Gdiplus::Bitmap UnlockTrayIcon(nTrayIconWidth, nTrayIconHeight, PixelFormat32bppARGB);
		std::shared_ptr<Gdiplus::Graphics> pgTrayIconLock(Gdiplus::Graphics::FromImage(&LockTrayIcon));
		std::shared_ptr<Gdiplus::Graphics> pgTrayIconUnlock(Gdiplus::Graphics::FromImage(&UnlockTrayIcon));

		Gdiplus::Rect destTrayIconRect{ 0, 0, nTrayIconWidth, nTrayIconHeight };
		pgTrayIconLock->DrawImage(pfsTrayIcon.get(), destTrayIconRect, 0, 0, nTrayIconWidth, nTrayIconHeight, Gdiplus::Unit::UnitPixel);
		pgTrayIconUnlock->DrawImage(pfsTrayIcon.get(), destTrayIconRect, nTrayIconWidth, 0, nTrayIconWidth, nTrayIconHeight, Gdiplus::Unit::UnitPixel);

		pRuntimeParams->LockTrayIcon.cbSize = sizeof(NOTIFYICONDATA);
		pRuntimeParams->LockTrayIcon.hWnd = ghWndIndicator;
		pRuntimeParams->LockTrayIcon.uID = nTrayID;
		pRuntimeParams->LockTrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
		pRuntimeParams->LockTrayIcon.uCallbackMessage = nTrayUM;
		LockTrayIcon.GetHICON(&pRuntimeParams->LockTrayIcon.hIcon);
		pRuntimeParams->UnlockTrayIcon.cbSize = sizeof(NOTIFYICONDATA);
		pRuntimeParams->UnlockTrayIcon.hWnd = ghWndIndicator;
		pRuntimeParams->UnlockTrayIcon.uID = nTrayID;
		pRuntimeParams->UnlockTrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
		pRuntimeParams->UnlockTrayIcon.uCallbackMessage = nTrayUM;
		UnlockTrayIcon.GetHICON(&pRuntimeParams->UnlockTrayIcon.hIcon);
	}

	// # Lock 与 Unlock 切换音效(支持 wav mp3 格式)
	ZeroMemory(szTemporaryData, sizeof(szTemporaryData));
	GetPrivateProfileString(pRuntimeParams->Section, TEXT("SoundEffect"), CString(pRuntimeParams->Section) + CString(TEXT(".wav")), szTemporaryData, _countof(szTemporaryData), strConfigPath);

	CString strSoundEffectPath = (_tcsstr(szTemporaryData, TEXT(":\\")) || _tcsstr(szTemporaryData, TEXT(":/"))) ? szTemporaryData : (CString(lpCurrentDirectory) + CString("\\") + szTemporaryData);

	// 打开音乐-播放音乐-停止音乐-关闭音乐
	if (PathFileExists(strSoundEffectPath))
	{
		CString strCommand;
		strCommand.Format(TEXT("open \"%s\" alias %s"), strSoundEffectPath.GetString(), pRuntimeParams->Section);
		mciSendString(strCommand, NULL, 0, NULL);
	}
	else
	{
		pRuntimeParams->Section = NULL;
	}

	if (wKeyState == 1)
	{
		pRuntimeParams->LockSet = LSFW_LOCK;
		if (pRuntimeParams->LockTrayIcon.hIcon)
		{
			Shell_NotifyIcon(NIM_ADD, &pRuntimeParams->LockTrayIcon);
		}
	}
	else if (wKeyState == 0)
	{
		pRuntimeParams->LockSet = LSFW_UNLOCK;
		if (pRuntimeParams->LockTrayIcon.hIcon)
		{
			Shell_NotifyIcon(NIM_ADD, &pRuntimeParams->UnlockTrayIcon);
		}
	}
}

VOID LoadLanguage(CONST TCHAR* lpCurrentDirectory)
{
	ATLASSERT(ghMenuIndicator != NULL);
	CString strConfigPath = CString(lpCurrentDirectory) + CString(TEXT("\\")) + CString(CONFIG_NAME);

	CONST TCHAR* lpLanguageSection = TEXT("Language");
	HMENU hMenuFile = GetSubMenu(ghMenuIndicator, 0);

	TCHAR szTemporaryData[MAX_PATH]{ 0 };
	GetPrivateProfileString(lpLanguageSection, TEXT("FileStart"), TEXT("自动运行"), szTemporaryData, _countof(szTemporaryData), strConfigPath);
	ModifyMenu(hMenuFile, ID_FILE_START, MF_BYCOMMAND, ID_FILE_START, szTemporaryData);

	GetPrivateProfileString(lpLanguageSection, TEXT("FileExit"), TEXT("安全退出"), szTemporaryData, _countof(szTemporaryData), strConfigPath);
	ModifyMenu(hMenuFile, ID_FILE_EXIT, MF_BYCOMMAND, ID_FILE_EXIT, szTemporaryData);

	GetPrivateProfileString(lpLanguageSection, TEXT("FileCaps"), TEXT("大写键锁"), gszFileCaps, _countof(gszFileCaps), strConfigPath);
	GetPrivateProfileString(lpLanguageSection, TEXT("FileNum"), TEXT("数字键锁"), gszFileNum, _countof(gszFileNum), strConfigPath);
}

INT_PTR CALLBACK IndicatorWndProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (uMsg)
	{
	case WM_INITDIALOG:
	{
		ghWndIndicator = hDlg;

		DWORD dwExStyle = GetWindowLong(hDlg, GWL_EXSTYLE);
		SetWindowLong(hDlg, GWL_EXSTYLE, dwExStyle | WS_EX_NOACTIVATE);

		TCHAR szCurrentDirectory[MAX_PATH];
		GetModuleFileName(NULL, szCurrentDirectory, MAX_PATH);
		*(_tcsrchr(szCurrentDirectory, TEXT('\\'))) = 0; // 把最后一个 \ 替换为 0

		LoadLanguage(szCurrentDirectory);
		LoadParameters(szCurrentDirectory, &gCapsLockSetup);
		LoadParameters(szCurrentDirectory, &gNumLockSetup);

		SetTimer(hDlg, IDEVENT_SCANNING, 10, NULL);
		return (INT_PTR)TRUE;
	}
	case WM_COMMAND:
	{
		if (wParam == ID_FILE_START)
		{
			HMENU hMenuFile = GetSubMenu(ghMenuIndicator, 0);
			DWORD dwChecked = CheckMenuItem(hMenuFile, ID_FILE_START, 0);
			if (dwChecked == MF_CHECKED)
			{
				CurrentVersionRun(IDACTION_CANCEL);
				CheckMenuItem(hMenuFile, ID_FILE_START, MF_UNCHECKED);
			}
			else if (dwChecked == MF_UNCHECKED)
			{
				CurrentVersionRun(IDACTION_APPEND);
				CheckMenuItem(hMenuFile, ID_FILE_START, MF_CHECKED);
			}
		}
		else if (wParam == ID_FILE_EXIT)
		{
			EndDialog(hDlg, lParam);
		}
		return (INT_PTR)TRUE;
	}
	case UMNOTIFY_CAPSLOCK:
	case UMNOTIFY_NUMLOCK:
	{
		if (LOWORD(lParam) == WM_RBUTTONUP)
		{
			SetForegroundWindow(hDlg);  // 没有这个菜单不会自动消失

			POINT destPopupPoint;
			GetCursorPos(&destPopupPoint);
			HMENU hMenuFile = GetSubMenu(ghMenuIndicator, 0);
			CheckMenuItem(hMenuFile, ID_FILE_START, CurrentVersionRun(IDACTION_CHECK) ? MF_CHECKED : MF_UNCHECKED);
			if (uMsg == UMNOTIFY_CAPSLOCK)
			{
				ModifyMenu(hMenuFile, ID_FILE_ABOUT, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED, ID_FILE_ABOUT, gszFileCaps);
			}
			else if (uMsg == UMNOTIFY_NUMLOCK)
			{
				ModifyMenu(hMenuFile, ID_FILE_ABOUT, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED, ID_FILE_ABOUT, gszFileNum);
			}
			TrackPopupMenu(hMenuFile, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_VERTICAL, destPopupPoint.x, destPopupPoint.y, 0, hDlg, NULL);
		}
		else if (LOWORD(lParam) == WM_LBUTTONUP)
		{
			DrawImageIcon((uMsg == UMNOTIFY_CAPSLOCK) ? &gCapsLockSetup : &gNumLockSetup);
		}
		return (INT_PTR)TRUE;
	}
	case WM_TIMER:
	{
		if (wParam == IDEVENT_DISPLAY)
		{
			KillTimer(hDlg, wParam);
			ShowWindowAsync(hDlg, SW_HIDE);
		}
		else if (wParam == IDEVENT_SCANNING)
		{

			SHORT wCapsLockSet = GetKeyState(VK_CAPITAL) ? LSFW_LOCK : LSFW_UNLOCK;
			SHORT wNumLockSet = GetKeyState(VK_NUMLOCK) ? LSFW_LOCK : LSFW_UNLOCK;
			if (gCapsLockSetup.IDEvent)
			{
				if (wCapsLockSet == LSFW_UNLOCK)
				{
					KillTimer(hDlg, gCapsLockSetup.IDEvent);
					gCapsLockSetup.IDEvent = 0;
				}
			}
			else if (gCapsLockSetup.Timeout > 0)
			{
				gCapsLockSetup.IDEvent = SetTimer(hDlg, IDCAPS_TIMEOUT, MILISECONDS(gCapsLockSetup.Timeout), NULL);
			}
			DrawImageIcon(&gCapsLockSetup, wCapsLockSet);
			if (gNumLockSetup.IDEvent)
			{
				if (wNumLockSet == LSFW_LOCK)
				{
					KillTimer(hDlg, gNumLockSetup.IDEvent);
					gNumLockSetup.IDEvent = 0;
				}
			}
			else if (gNumLockSetup.Timeout > 0)
			{
				gNumLockSetup.IDEvent = SetTimer(hDlg, IDNUM_TIMEOUT, MILISECONDS(gNumLockSetup.Timeout), NULL);
			}
			DrawImageIcon(&gNumLockSetup, wNumLockSet);
		}
		else if (wParam == IDCAPS_TIMEOUT)
		{
			KillTimer(hDlg, wParam);
			gCapsLockSetup.IDEvent = 0;
			keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 1);
			keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 1);
		}
		else if (wParam == IDNUM_TIMEOUT)
		{
			KillTimer(hDlg, wParam);
			gNumLockSetup.IDEvent = 0;
			keybd_event(VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
			keybd_event(VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
		}
		return (INT_PTR)TRUE;
	}
	case WM_CLOSE:
	{
		EndDialog(hDlg, lParam);
		return (INT_PTR)TRUE;
	}
	case WM_NCDESTROY:
	{
		PostQuitMessage(0);
		return (INT_PTR)TRUE;
	}
	}
	return (INT_PTR)FALSE;
}

BOOL CurrentVersionRun(UINT uMsg)
{
	HKEY hRegKey{ NULL };
	LSTATUS lRetValue = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), 0, KEY_ALL_ACCESS, &hRegKey);
	if (lRetValue == ERROR_SUCCESS) ///打开启动项       
	{
		if (uMsg == IDACTION_CANCEL)
		{
			lRetValue = RegDeleteValue(hRegKey, PACKAGE_NAME);
		}
		else
		{
			TCHAR szAppPath[MAX_PATH]{ 0 };
			GetModuleFileName(NULL, szAppPath, MAX_PATH);
			if (uMsg == IDACTION_APPEND)
			{
				lRetValue = RegSetValueEx(hRegKey, PACKAGE_NAME, 0, REG_SZ, (LPBYTE)szAppPath, lstrlen(szAppPath) * sizeof(TCHAR));
			}
			else
			{
				DWORD dwRegType = 0;
				TCHAR szRegValue[MAX_PATH]{ 0 };
				DWORD dwRegValueLen = _countof(szRegValue) * sizeof(TCHAR);
				lRetValue = RegQueryValueEx(hRegKey, PACKAGE_NAME, NULL, &dwRegType, (LPBYTE)szRegValue, &dwRegValueLen); // 支持 XP 32 位系统

				if (lRetValue == ERROR_SUCCESS)
				{
					if (_tcsicmp(szAppPath, szRegValue) != 0)
					{
						lRetValue = (LSTATUS)-1; // 注册表保存的并非当前应用程序路径
					}
				}
			}
		}

		RegCloseKey(hRegKey);
	}
	return (lRetValue == ERROR_SUCCESS);
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPTSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	WPARAM dwExitCode = 0;
	HANDLE hSingletonMutex = CreateMutex(NULL, FALSE, TEXT("COM.HNSFNET.KEYLOCK"));
	if (hSingletonMutex)
	{
		DWORD dwWaitResult = WaitForSingleObject(hSingletonMutex, 0);
		if (dwWaitResult == WAIT_OBJECT_0 || dwWaitResult == WAIT_ABANDONED)
		{
			// GDI+ Startup
			ULONG_PTR gdiStartupToken;
			Gdiplus::GdiplusStartupInput gdiStartupInput;
			Gdiplus::GdiplusStartup(&gdiStartupToken, &gdiStartupInput, NULL);

			ghMenuIndicator = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_KEYLOCK));

			WNDCLASSEX WndClassEx;
			WndClassEx.cbSize = sizeof(WNDCLASSEX);
			GetClassInfoEx(NULL, WC_DIALOG, &WndClassEx);
			WndClassEx.lpszClassName = TEXT("KEYLOCK");
			WndClassEx.style &= ~CS_GLOBALCLASS;
			RegisterClassEx(&WndClassEx);

			DialogBox(hInstance, MAKEINTRESOURCE(IDD_KEYLOCK), NULL, IndicatorWndProc);

			MSG msgThread;
			while (GetMessage(&msgThread, NULL, 0, 0))
			{
				TranslateMessage(&msgThread);
				DispatchMessage(&msgThread);
			}
			dwExitCode = msgThread.wParam;

			//  GDI+ Shutdown
			Gdiplus::GdiplusShutdown(gdiStartupToken);

			ReleaseMutex(hSingletonMutex);
		}
		CloseHandle(hSingletonMutex);
	}
	return (int) dwExitCode;
}
